using System;
using System.Collections;
using System.Reflection;
using System.Collections.Generic;
using System.Text;
using DCRF.Common.Primitive;
using DCRF.Common.Collection;
using DCRF.Common.Helper;
using DCRF.Common.Interface;
using DCRF.Common.Definition;
using DCRF.Common.Connection;
using DCRF.Common.Attributes;
using DCRF.Common.Support;
using DCRF.Common.Connection.Wrappers;

namespace DCRF.Common.Core
{
    public abstract class BlockBase : IBlock, IContainerBlock, IContainedBlock
    {
        #region Private fields

        private bool enableLog = false;

        protected IContainerBlockWeb blockWeb = null;
        /// <summary>
        /// Key is a string and value is a Connector object
        /// </summary>
        protected Dictionary<string, Connector> internalConnectors = new Dictionary<string, Connector>();

        //just a tiny accessor to connectors provided to the block (not outer world)
        //protected BlockConnectors Connectors = null;
        //private List<string> services = null;

        private List<string> serviceCache = null;
        private Dictionary<string, MethodBase> serviceMethodCache = null;

        /// <summary>
        /// If a block is composed of several internal blocks, this innerWeb is used to host
        /// child blocks.
        /// </summary>
        protected BlockWeb innerWeb = null;

        /// <summary>
        /// The unique id assigned to this block in the web when loading
        /// </summary>
        protected Guid myId = Guid.Empty;

        #endregion

        #region Abstract items 

        //public abstract BlockInfo BlockInfo
        //{
        //    get;
        //}
     
        #endregion 

        #region Constructor

        public BlockBase(Guid id, IContainerBlockWeb parent)
        {
            myId = id;
            blockWeb = parent;
            //connectors = new ConnectorCollection(parent);
            //Connectors = new BlockConnectors(this);
            
        }

        #endregion

        #region Properties

        public Guid Id
        {
            get
            {
                return myId;
            }
        }

        #endregion

        #region Initialization and dispose methods
        /// <summary>
        /// Initializes Block - this method is called after component is being BlockWebd.
        /// </summary>
        public virtual void InitBlock()
        {
            //TODO: add connectors using attributes defined

        }


        public virtual void Dispose()
        {
            //dispose innerweb if not null
            if (innerWeb != null)
            {
                innerWeb.Dispose();
            }
        }

        #endregion

        #region Protected helper methods and properties

        /// <summary>
        /// This provides an interface to outer world to attach and detach their endpoints to my connectors.
        /// So I can invoke them when required.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public IConnector this[string key]
        {
            get
            {
                logEvent(LogType.Connector, "Get Connector: "+key+" on " + Id.ToString());

                if (!internalConnectors.ContainsKey(key))
                {
                    internalConnectors.Add(key, new Connector(key, blockWeb, false));
                }

                //in any case, I will have a local connector with given key,
                //this connector will invoke web's globalConnector if applicable
                return internalConnectors[key];
            }
        }

        /// <summary>
        /// Look into blocks of innerWeb
        /// </summary>
        /// <param name="Guid"></param>
        /// <returns></returns>
        public IBlock this[Guid id]
        {
            get
            {
                //if block is not found in innerWeb, it will lookup its parent, so check first
                if (innerWeb != null && innerWeb.BlockHandles.Contains(id))
                {
                    return innerWeb[id];
                }

                return null;
            }
        }

        
        #endregion

        #region meta services

        [BlockService]
        public object ProcessMetaInfo(BlockMetaInfoType type, string itemNam, object[] args)
        {
            switch (type)
            {
                case BlockMetaInfoType.ConnectorEndpoints: return internalConnectors[itemNam].GetEndPointsDescription();
                case BlockMetaInfoType.ConnectorInfo: return DCRFHelper.GetConnectorInfo(this, itemNam);
                case BlockMetaInfoType.ConnectorKeys: return getConnectorKeys();
                case BlockMetaInfoType.InnerWebHost: return getInnerWebHost(); 
                case BlockMetaInfoType.InnerWebId: return getInnerWebId(); 
                case BlockMetaInfoType.InnerWebPort: return getInnerWebPort();
                case BlockMetaInfoType.ServiceInfo: return DCRFHelper.GetMethodInfo(this, itemNam); ; 
                case BlockMetaInfoType.Services: return getServices();
                case BlockMetaInfoType.BlockInfo: return DCRFHelper.GetBlockInfo(this);
                case BlockMetaInfoType.ServiceArgsInfo: return DCRFHelper.GetServiceArgsInfo(this, itemNam);
            }

            return null;
        }

        [BlockService]
        public object ProcessMetaService(BlockMetaServiceType type, string itemNam, object[] args)
        {
            switch (type)
            {
                case BlockMetaServiceType.DisableLog: enableLog = false; return "Log Disabled"; 
                case BlockMetaServiceType.EnableLog: enableLog = true; return "Log Enabled"; 
                case BlockMetaServiceType.GetInnerWeb: return innerWeb;
                case BlockMetaServiceType.InvokeConnector: return processLocalConnector(itemNam, args);
            }

            return null;
        }

        private List<string> getServices()
        {
            if (serviceCache == null)
            {
                serviceMethodCache = BlockHelper.GetServices(this);
                serviceCache = new List<string>();

                foreach (MethodBase mb in serviceMethodCache.Values)
                {
                    serviceCache.Add(mb.Name);
                }
            }

            return serviceCache;            
        }

        private string getInnerWebHost()
        {
            if (innerWeb == null) return null;

            return innerWeb.Host;
        }

        private int getInnerWebPort()
        {
            if (innerWeb == null) return -1;

            return innerWeb.Port;
        }

        private string getInnerWebId()
        {
            if (innerWeb == null) return null;

            return innerWeb.Id;
        }

        private ICollection<string> getConnectorKeys()
        {
            //connectors are created in localConnecotrs upon block initialization
            return internalConnectors.Keys;

            //List<string> standards = BlockHelper.GetConnectors(this);

            //foreach (string item in localConnectors.Keys)
            //{
            //    if ( !standards.Contains(item) ) standards.Add(item);
            //}

            //return standards;
        }

        private List<object> processLocalConnector(string connectorKey, params object[] args)
        {
            if (internalConnectors.ContainsKey(connectorKey))
            {
                return internalConnectors[connectorKey].ProcessRequestAll(args);
            }

            return null;
        }

        #endregion

        public IBlock GetParentWebBlock(Guid handle)
        {
            if (blockWeb != null)
            {
                return blockWeb[handle];
            }

            return null;
        }

        
        public object ProcessRequest(string serviceName, params object[] args)
        {
            try
            {
                this.getServices();

                object result = BlockHelper.ProcessRequest(serviceMethodCache, this, serviceName, args);

                if (enableLog)
                {
                    //this call is time consuming so we set a flag for it
                    logEvent(LogType.ProcessRequest, "Done Process - " + serviceName + " on " + Id.ToString() + " - Result = " + (result == null ? "(null)" : result.ToString()));
                }

                return result;
            }
            catch (Exception exc)
            {
                logEvent(LogType.Exception, "ProcessRequest Exception for "+serviceName+" : "+exc.Message);
                throw;
            }
        }

        public virtual object OnBeforeMigration(ref bool cancelOperation)
        {
            return null;
        }

        public virtual void OnAfterMigration(object state)
        {
        }

        public object OnBeforeReload(ref bool cancelOperation)
        {
            return null;
        }

        public void OnAfterReload(object state)
        {
        }

        private void logEvent(LogType type, string log)
        {
            if (enableLog)
            {
                blockWeb[EventCode.LogWebEvent].Raise(type, log);
            }
        }
    }
}
